/*
This file is part of Docking@Home.
http://docking.gcl.cis.udel.edu
Copyright (C) 2009 University of Delaware

Docking@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Docking@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Docking@Home.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
	Filename: TabbedGUI.cpp
	Description: A high level class that will contain all information about
		our tabbed GUI layout and will handle events sent to it.
		- Implements all functions described in the header file
	Revision History: 
		Created - 11/19/07
		Made into the window manager - 01/02/08
	Author: Robert Keller (11/19/07 -> current)
*/

#include <vector>

using std::vector;
using std::iterator;

#include "TabbedGUI.h"
#include "LowLevelTab.h"
#include "HighLevelTab.h"

#include "parse.h"
#include "gutil.h"
#include "boinc_gl.h"
#include "txf_util.h"
#include "filesys.h"

// ------------------------------------------------------------
// Static Variable Instantiation
// ------------------------------------------------------------

GLHelper * TabbedGUI::glHelper;

// ------------------------------------------------------------
// Constructor / Destructor
// ------------------------------------------------------------

TabbedGUI::TabbedGUI() 
	: LOGO_SIZE_FACTOR(7.0f)
{
	currentTab = 0;
	wuName[0] = '\0';
}// TabbedGUI

TabbedGUI::~TabbedGUI()
{
	for (unsigned int i = 0; i < tabs.size(); i++)
		delete tabs[i];

	tabs.clear();

	glHelper->cleanup();

	//delete the news ticker
	delete this->ticker;
}// ~TabbedGUI

// ------------------------------------------------------------
// Initialization Functions
// ------------------------------------------------------------

// Initializes the Tabbed GUI with a default border color of white
void TabbedGUI::init (APP_INIT_DATA uc_aid)
{
	init(glHelper->WHITE, glHelper->WHITE, uc_aid);
}// init

void TabbedGUI::init (const float border[4], const float complex[4],  APP_INIT_DATA uc_aid)
{		
	char path[256];

	// Complex Text Properties
	complexTextXStandardScale = 0.80f;
	complexTextYStandardScale = -0.80f;

	complexTextXScale = complexTextXStandardScale;
	complexTextYScale = complexTextYStandardScale;

	glHelper->copyColor4f(complexTextColor, complex);

	// Set Workunit name
	setWUName(uc_aid.wu_name);

	// Make sure fonts are loaded
	glHelper->loadDefaultFonts();

	int test = boinc_resolve_filename("logo.jpg", path, sizeof(path));

	logo = Logo(path);

	// Init Camera
	glHelper->initCamera(45.0, 1.0, 1.0, 3000.0);

	// Light properties
	float ambient[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	float position[4] = { -0.0f, 0.0f, 50.0f, 0.0f };
	float direction[4] = { -0.0f, -0.0f, -3.0f, 0.0f };

	glHelper->initLights(ambient, position, direction);

	// Initialize the Tab Window
	tabWindow.setBorderColor(border);
	tabWindow.setWindowWidth(tabWindow.STANDARD_WIDTH);
	tabWindow.setWindowHeight(tabWindow.STANDARD_HEIGHT);
	tabWindow.setLogoOffset(
		(int)(tabWindow.STANDARD_HEIGHT / LOGO_SIZE_FACTOR));
	tabWindow.resetCorners();

	// Set current tab

	currentTab = 0;

	// Add the initial tabs

	LowLevelTab * lowLevelTab = new LowLevelTab("View");

	// Initialize with needed handles
	lowLevelTab->init(&tabWindow, 30.0f);

	// Set Resource Information
	lowLevelTab->initResourceInfo(uc_aid.user_name,	uc_aid.team_name);

	// Set Indentation Properties and Alpha

	lowLevelTab->setNameIndentX(16.0f);
	lowLevelTab->setNameIndentY(-2.0f);

	// Set Text Alpha Blending Values

	lowLevelTab->setTextSelectedAlpha(0.1f);
	lowLevelTab->setTextUnselectedAlpha(0.1f);
	lowLevelTab->setTextHoveredAlpha(0.1f);

	// Set Colors

	lowLevelTab->setSelectedBoxColor(glHelper->WHITE);
	lowLevelTab->setUnselectedBoxColor(glHelper->WHITE);
	lowLevelTab->setHoveredBoxColor(glHelper->WHITE);

	lowLevelTab->setSelectedTextColor(glHelper->BLACK);
	lowLevelTab->setUnselectedTextColor(glHelper->WHITE);

	// Select the tab 

	lowLevelTab->select();

	// Create a high level tab
	
	HighLevelTab * highLevelTab = new HighLevelTab("Info");

	// Add needed handles

	highLevelTab->setTabWindow(&tabWindow);
	
	// Set Indentation Properties

	highLevelTab->setNameIndentX(16.0f);
	highLevelTab->setNameIndentY(-2.0f);

	// Set Text Alpha Blending Values

	highLevelTab->setTextSelectedAlpha(0.1f);
	highLevelTab->setTextUnselectedAlpha(0.1f);
	highLevelTab->setTextHoveredAlpha(0.1f);

	// Set colors

	highLevelTab->setSelectedBoxColor(glHelper->WHITE);
	highLevelTab->setUnselectedBoxColor(glHelper->WHITE);
	highLevelTab->setHoveredBoxColor(glHelper->WHITE);

	highLevelTab->setSelectedTextColor(glHelper->BLACK);
	highLevelTab->setUnselectedTextColor(glHelper->WHITE);

	// Add high level tab

	addTab(highLevelTab);

	// Add the low level tab

	addTab(lowLevelTab);

	//get the url from the project directory
	string url=uc_aid.project_dir;
	url=url.substr(url.rfind('/')+1,string::npos);
	string dir="";
	size_t index=url.find('_');
	if(index!=string::npos){
		dir=url.substr(index,string::npos);
		url=url.substr(0,index);		
		for(size_t i=0;i<dir.length();i++)
			if(dir[i]=='_')
				dir[i]='/';
	}
	dir+="/ticker.php";

	//get the news ticker
	this->ticker=new NewsTicker(url,dir);

/*printf("<app_init_data>\n"
        "<major_version>%d</major_version>\n"
        "<minor_version>%d</minor_version>\n"
        "<release>%d</release>\n"
        "<app_version>%d</app_version>\n",
        uc_aid.major_version,
        uc_aid.minor_version,
        uc_aid.release,
        uc_aid.app_version
    );
    if (strlen(uc_aid.app_name)) {
        printf( "<app_name>%s</app_name>\n", uc_aid.app_name);
    }
    if (strlen(uc_aid.symstore)) {
        printf( "<symstore>%s</symstore>\n", uc_aid.symstore);
    }
    if (strlen(uc_aid.acct_mgr_url)) {
        printf( "<acct_mgr_url>%s</acct_mgr_url>\n", uc_aid.acct_mgr_url);
    }
    if (uc_aid.project_preferences && strlen(uc_aid.project_preferences)) {
        printf( "<project_preferences>\n%s</project_preferences>\n", uc_aid.project_preferences);
    }
    if (strlen(uc_aid.team_name)) {
        printf( "<team_name>%s</team_name>\n", uc_aid.team_name);
    }
    if (strlen(uc_aid.user_name)) {
        printf( "<user_name>%s</user_name>\n", uc_aid.user_name);
    }
    if (strlen(uc_aid.project_dir)) {
        printf( "<project_dir>%s</project_dir>\n", uc_aid.project_dir);
    }
    if (strlen(uc_aid.boinc_dir)) {
        printf( "<boinc_dir>%s</boinc_dir>\n", uc_aid.boinc_dir);
    }
    if (strlen(uc_aid.authenticator)) {
        printf( "<authenticator>%s</authenticator>\n", uc_aid.authenticator);
    }
    if (strlen(uc_aid.wu_name)) {
        printf( "<wu_name>%s</wu_name>\n", uc_aid.wu_name);
    }
#ifdef _WIN32
    if (strlen(uc_aid.shmem_seg_name)) {
        printf( "<comm_obj_name>%s</comm_obj_name>\n", uc_aid.shmem_seg_name);
    }
#else
    printf( "<shm_key>%d</shm_key>\n", uc_aid.shmem_seg_name);
#endif
    printf(
        "<slot>%d</slot>\n"
        "<wu_cpu_time>%f</wu_cpu_time>\n"
        "<user_total_credit>%f</user_total_credit>\n"
        "<user_expavg_credit>%f</user_expavg_credit>\n"
        "<host_total_credit>%f</host_total_credit>\n"
        "<host_expavg_credit>%f</host_expavg_credit>\n"
        "<resource_share_fraction>%f</resource_share_fraction>\n"
        "<checkpoint_period>%f</checkpoint_period>\n"
        "<fraction_done_start>%f</fraction_done_start>\n"
        "<fraction_done_end>%f</fraction_done_end>\n"
        "<rsc_fpops_est>%f</rsc_fpops_est>\n"
        "<rsc_fpops_bound>%f</rsc_fpops_bound>\n"
        "<rsc_memory_bound>%f</rsc_memory_bound>\n"
        "<rsc_disk_bound>%f</rsc_disk_bound>\n"
        "<computation_deadline>%f</computation_deadline>\n",
        uc_aid.slot,
        uc_aid.wu_cpu_time,
        uc_aid.user_total_credit,
        uc_aid.user_expavg_credit,
        uc_aid.host_total_credit,
        uc_aid.host_expavg_credit,
        uc_aid.resource_share_fraction,
        uc_aid.checkpoint_period,
        uc_aid.fraction_done_start,
        uc_aid.fraction_done_end,
        uc_aid.rsc_fpops_est,
        uc_aid.rsc_fpops_bound,
        uc_aid.rsc_memory_bound,
        uc_aid.rsc_disk_bound,
        uc_aid.computation_deadline
    );

	printf(
        "<proxy_info>\n"
        "%s"
        "%s"
        "%s"
        "    <socks_version>%d</socks_version>\n"
        "    <socks_server_name>%s</socks_server_name>\n"
        "    <socks_server_port>%d</socks_server_port>\n"
        "    <http_server_name>%s</http_server_name>\n"
        "    <http_server_port>%d</http_server_port>\n"
        "    <socks5_user_name>%s</socks5_user_name>\n"
        "    <socks5_user_passwd>%s</socks5_user_passwd>\n"
        "    <http_user_name>%s</http_user_name>\n"
        "    <http_user_passwd>%s</http_user_passwd>\n"
        "</proxy_info>\n",
		uc_aid.proxy_info.use_http_proxy?"    <use_http_proxy/>\n":"",
        uc_aid.proxy_info.use_socks_proxy?"    <use_socks_proxy/>\n":"",
        uc_aid.proxy_info.use_http_auth?"    <use_http_auth/>\n":"",
        uc_aid.proxy_info.socks_version,
        uc_aid.proxy_info.socks_server_name,
        uc_aid.proxy_info.socks_server_port,
        uc_aid.proxy_info.http_server_name,
        uc_aid.proxy_info.http_server_port,
		uc_aid.proxy_info.socks5_user_name,
		uc_aid.proxy_info.socks5_user_passwd,
		uc_aid.proxy_info.http_user_name,
		uc_aid.proxy_info.http_user_passwd
    );*/

}// init

// ------------------------------------------------------------
// Tab Functions
// ------------------------------------------------------------


// Name: addTab
// Description: Adds a tab at the preferred location in the list 
//	if that location can be used.
// Postconditions:
//	- If the preferred index is beyond the list, it will add the tab
//		to the end of the list
//	- If the preferred index is in use, it will add it at that place
//		and shift the rest of the list
// Returns: the index of where the tab was added
int TabbedGUI::addTab (Tab * tab, unsigned int preferredIndex)
{
	if (preferredIndex >= tabs.size())
	{
		preferredIndex = tabs.size();
		tabs.push_back(tab);
	}// if
	else
	{
		vector<Tab *>::iterator iter=tabs.begin();
		
		for (unsigned int i = 0; i < preferredIndex; i++)
			iter++;

		tabs.insert(iter, tab);
	}

	return preferredIndex;
}// addTab


Tab * TabbedGUI::removeTab (int num) 
{
	return NULL;
}// removeTab

// ------------------------------------------------------------
// Tab Window Functions
// ------------------------------------------------------------

void TabbedGUI::resize (int w, int h, bool bFullscreen)
{
	tabWindow.setWindowWidth(w);
	tabWindow.setWindowHeight(h);
	tabWindow.setLogoOffset((int)(h / LOGO_SIZE_FACTOR));
	tabWindow.resetCorners();

	for (unsigned int i = 0; i < tabs.size(); i++)
		tabs[i]->resizeScales(bFullscreen);

	complexTextXScale = tabWindow.getWidthFactor() 
		* complexTextXStandardScale;
	complexTextYScale = tabWindow.getHeightFactor() 
		* complexTextYStandardScale;

}// void

// ------------------------------------------------------------
// Render Functions
// ------------------------------------------------------------

void TabbedGUI::draw (double screenWidth, double screenHeight,
	bool bFullScreen)
{
	if (bFullScreen)
		drawScreenSaver (screenWidth, screenHeight);
	else
		drawGUI (screenWidth, screenHeight);

}// draw

void TabbedGUI::drawGUI (double screenWidth, double screenHeight)
{
	int tempWidth;
	int max_ascent;
	int max_descent;
	int tempHeight;
	char textBuf[40];

	float logoOffset = (float)screenHeight / LOGO_SIZE_FACTOR;
	
	glHelper->setOrthographic();

	logo.draw(0, 0, (float)screenWidth, logoOffset);	

	tabWindow.draw();

	// Draw Name of Complex
	strcpy(textBuf, "Complex: ");
	strcat(textBuf, wuName);
	
	glHelper->getDrawnTextProperties (textBuf, 0, &tempWidth, 
		&max_ascent, &max_descent);
		
	tempHeight = max_ascent + max_descent;

	//complex name
	glHelper->drawText(.1f,tabWindow.getBottomRightX()-(tempWidth + 8)*complexTextXScale,tabWindow.getTopLeftY(),0.f,complexTextXScale,complexTextYScale,1.f,complexTextColor,0,textBuf);

	//news
	this->ticker->Tick();
	glHelper->drawText(.1f,tabWindow.getTopLeftX(),tabWindow.getBottomRightY()*1.05f,0.f,complexTextXScale*.65f,complexTextYScale*.65f,1.f,glHelper->YELLOW,0,this->ticker->getNews());

	glHelper->setOrthographicDone();

	for (unsigned int i = 0; i < tabs.size(); i++)
	{
		tabs[i]->draw(i, false);
	}// for


}// drawGUI

void TabbedGUI::drawScreenSaver (double screenWidth,double screenHeight)
{
	glHelper->setOrthographic();
		
	float logoOffset = (float)screenHeight / LOGO_SIZE_FACTOR;
	logo.draw(0, 0, (float)screenWidth, logoOffset);

	//show the news ticker
	this->ticker->Tick();
	glHelper->drawText(.1f,tabWindow.getTopLeftX(),tabWindow.getBottomRightY(),0.f,complexTextXScale*.65f,complexTextYScale*.65f,1.f,glHelper->YELLOW,0,this->ticker->getNews());

	glHelper->setOrthographicDone();	

	// Draw Low Level Tab in full screen mode to display only 
	// the complex
	tabs[0]->draw(0, true);

}// drawScreenSaver

// ------------------------------------------------------------
// Input Functions
// ------------------------------------------------------------

void TabbedGUI::handleMouseMove (int x, int y, int left, int middle, int right)
{
	// Check if button click was to change tab
	handleTabMouseInteraction(x, y, left!=0);

	// Let current tab handle event
	tabs[currentTab]->handleMouseMove (x, y, left, middle, right);
}// handleMouseMove

void TabbedGUI::handleMouseButton (int x, int y, int which,	int is_down)
{
	bool bLeftClicked = false;

	if (is_down)
	{
		// Left mouse button
		if (which == 0)
		{		
			bLeftClicked = true;
		}// if
	}// if

	handleTabMouseInteraction(x, y, bLeftClicked);

	// Let current tab handle event
	tabs[currentTab]->handleMouseButton (x, y, which, is_down);
}// handleMouseButton

void TabbedGUI::handleKeyPress (int key1, int key2)
{
	// Check if key was to change a gui-wide feature

	// Let current tab handle event
	tabs[currentTab]->handleKeyPress(key1, key2);
}// handleKeyPress

void TabbedGUI::handleKeyRelease (int key1, int key2)
{
	// Check if key was to change a gui-wide feature
	
	// Let current tab handle event
	tabs[currentTab]->handleKeyRelease(key1, key2);
}// handleKeyPress

// ------------------------------------------------------------
// Set Workunit Name
// ------------------------------------------------------------

void TabbedGUI::setWUName (char * wu_name)
{
	int underscoreLoc; // counter variable
	int len = strlen(wu_name);
	
	if (len > MAX_WORKUNIT_NAME_LENGTH)
		len = MAX_WORKUNIT_NAME_LENGTH;

	for (underscoreLoc = 0; underscoreLoc < len; underscoreLoc++)
		if (wu_name[underscoreLoc] == '_')
			break;

	strncpy(wuName, wu_name, underscoreLoc);
	wuName[underscoreLoc] = '\0';

}// setWUName

// ------------------------------------------------------------
// Tab Interaction Functions
// ------------------------------------------------------------

void TabbedGUI::handleTabMouseInteraction (int x, int y, bool leftClicked)
{
	unsigned int i;
	
	// Index of the newly selected button. Used to unselect the 
	// other buttons (if any were currently selected)
	int newSelected = -1; 
	
	// Check the tabs for mouse interaction
	for (i = 0; i < tabs.size(); i++)
	{
		if (tabs[i]->determineState(i, x, y, leftClicked))
		{
			newSelected = i;
			break;
		}// if
	}// for

	// Unselect old selected tab if a new one is selected
	if (newSelected != -1)
	{
		tabs[currentTab]->unselect();

		currentTab = newSelected;

		tabs[currentTab]->select();
	}// if

}// handleTabMouseInteraction
